'''
This file is part of ModeliMark.

ModeliMark is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

ModeliMark is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with ModeliMark.
If not, see <http://www.gnu.org/licenses/>.

Copyright 2011 Jens Frenkel
'''

import basemodelgenerator
import os

class ClassofClass(basemodelgenerator.BaseModelGenerator):
    '''
    classdocs
    '''
    
    # Ground 4
    # ModelMassen
    # 1 17
    # 2 38
    # 3 80
    # --> *2 + 2
    
    # ModelClass
    # Ground + ModelMassen 
    
    # 2    42
    # 3    84
    # 4    168
    # 5    336
    # 6    672
    # 7    1344
    # 8    2688
    # 9    5376
    # 10    10752
    # 11    21504
    # 12    43008
    # 13    86016
    # 14    172032
    # 15    344064
    # 16    688128
    # 17    1376256 

    def __init__(self):
        '''
        Constructor
        '''
                
    def generateModelSource(self, Number, dir):  
               
        assert(isinstance(Number,int))
        assert(isinstance(dir,str))
        
        modelname = 'modelClass_%s'%(Number)
        workdir = '%s/%s'%(dir,modelname) 

        # new directory
        if not os.access(workdir, os.F_OK):
            os.mkdir(workdir)        
        
        files = self._generateGround(workdir)
        
        files = self._generateMassenModel(files,Number,workdir)
        
        files = self._generateModelClass(files,modelname,Number,workdir)
        
        return modelname, files, workdir

    def _generateMassenModel(self,files,Number,workdir):
        
        if (Number > 1):
            filename = '%s/modelMassen_%s.mo'%(workdir,str(Number))
            f = open( filename, 'w' )
            f.write('model Massen_%s\n'%(str(Number)))
            f.write('  Massen_%s massen_a;\n'%(str(Number-1)))
            f.write('  Massen_%s massen_b;\n'%(str(Number-1)))
            f.write('  Modelica.Mechanics.Translational.Interfaces.Flange_b flange_b;\n')
            f.write('  Modelica.Mechanics.Translational.Interfaces.Flange_a flange_a;\n')
            f.write('equation\n')
            f.write('  connect(flange_a, massen_a.flange_a);\n')
            f.write('  connect(massen_a.flange_b, massen_b.flange_a);\n')
            f.write('  connect(massen_b.flange_b, flange_b);\n')
            f.write('end Massen_%s;'%(str(Number)))   
            f.close()       
            files.append(filename)
            # maybe python has also tail recursive feature
            return self._generateMassenModel(files,Number-1,workdir)
        else: 
            filename = '%s/modelMassen_1.mo'%(workdir) 
            f = open( filename, 'w' )
            f.write('model Massen_1\n')
            f.write('  Modelica.Mechanics.Translational.Components.Mass mass(m=1);\n')
            f.write('  Modelica.Mechanics.Translational.Components.Spring spring(c=1);\n')
            f.write('  Modelica.Mechanics.Translational.Interfaces.Flange_a flange_a;\n')
            f.write('  Modelica.Mechanics.Translational.Interfaces.Flange_b flange_b;\n')
            f.write('equation\n')
            f.write('  connect(spring.flange_b, mass.flange_a);\n')
            f.write('  connect(flange_a, spring.flange_a);\n')
            f.write('  connect(mass.flange_b, flange_b);\n')
            f.write('end Massen_1;\n')
            f.close()
            files.append(filename)
            return files;

        
    def _generateModelClass(self,files,modelname,Number,workdir):
        
        filename = '%s/%s.mo'%(workdir,modelname) 
        f = open(filename, 'w' )
        f.write('model %s\n'%(modelname))
        f.write('  Ground ground;\n')
        f.write('  Massen_%s massen_%s_;\n'%(str(Number),str(Number)))
        f.write('equation\n')
        f.write('  connect(ground.flange_b, massen_%s_.flange_a);\n'%(str(Number)))
        f.write('end %s;\n'%(modelname))
        f.close()   
        files.append(filename)
        return files    
    
    def _generateGround(self,workdir):

        filename = '%s/Ground.mo'%(workdir) 
        f = open(filename, 'w' )
        f.write('package Modelica "Modelica Standard Library (Version 3.1)"\n')
        f.write('extends Modelica.Icons.Library;\n')
        f.write('\n')
        f.write('  package Mechanics\n')
        f.write('  "Library of 1-dim. and 3-dim. mechanical components (multi-body, rotational, translational)"\n')
        f.write('  extends Modelica.Icons.Library2;\n')
        f.write('\n')
        f.write('    package Translational\n')
        f.write('    "Library to model 1-dimensional, translational mechanical systems"\n')
        f.write('      extends Modelica.Icons.Library2;\n')
        f.write('      import SI = Modelica.SIunits;\n')
        f.write('\n')
        f.write('      package Components\n')
        f.write('      "Components for 1D translational mechanical drive trains"\n')
        f.write('        extends Modelica.Icons.Library2;\n')
        f.write('\n')
        f.write('        model Fixed "Fixed flange"\n')
        f.write('          parameter SI.Position s0=0 "fixed offset position of housing";\n')
        f.write('\n')
        f.write('          Interfaces.Flange_b flange;\n')
        f.write('        equation\n')
        f.write('          flange.s = s0;\n')
        f.write('        end Fixed;\n')
        f.write('\n')
        f.write('        model Mass "Sliding mass with inertia"\n')
        f.write('          parameter SI.Mass m(min=0, start=1) "mass of the sliding mass";\n')
        f.write('          parameter StateSelect stateSelect=StateSelect.default\n')
        f.write('          "Priority to use s and v as states";\n')
        f.write('          extends Translational.Interfaces.PartialRigid(L=0,s(start=0, stateSelect=stateSelect));\n')
        f.write('          SI.Velocity v(start=0, stateSelect=stateSelect)\n')
        f.write('          "absolute velocity of component";\n')
        f.write('          SI.Acceleration a(start=0) "absolute acceleration of component";\n')
        f.write('\n')
        f.write('        equation\n')
        f.write('          v = der(s);\n')
        f.write('          a = der(v);\n')
        f.write('          m*a = flange_a.f + flange_b.f;\n')
        f.write('        end Mass;\n')
        f.write('\n')
        f.write('        model Spring "Linear 1D translational spring"\n')
        f.write('          extends Translational.Interfaces.PartialCompliant;\n')
        f.write('          parameter SI.TranslationalSpringConstant c(final min=0, start = 1)\n')
        f.write('          "spring constant ";\n')
        f.write('          parameter SI.Distance s_rel0=0 "unstretched spring length";\n')
        f.write('\n')
        f.write('        equation\n')
        f.write('          f = c*(s_rel - s_rel0);\n')
        f.write('        end Spring;\n')
        f.write('      end Components;\n')
        f.write('\n')
        f.write('      package Interfaces\n')
        f.write('      "Interfaces for 1-dim. translational mechanical components"\n')
        f.write('          extends Modelica.Icons.Library;\n')
        f.write('\n')
        f.write('        connector Flange_a\n')
        f.write('        "(left) 1D translational flange (flange axis directed INTO cut plane, e. g. from left to right)"\n')
        f.write('\n')
        f.write('          SI.Position s "absolute position of flange";\n')
        f.write('          flow SI.Force f "cut force directed into flange";\n')
        f.write('        end Flange_a;\n')
        f.write('\n')
        f.write('        connector Flange_b\n')
        f.write('        "right 1D translational flange (flange axis directed OUT OF cut plane)"\n')
        f.write('\n')
        f.write('          SI.Position s "absolute position of flange";\n')
        f.write('          flow SI.Force f "cut force directed into flange";\n')
        f.write('        end Flange_b;\n')
        f.write('\n')
        f.write('        partial model PartialRigid\n')
        f.write('        "Rigid connection of two translational 1D flanges "\n')
        f.write('          SI.Position s\n')
        f.write('          "Absolute position of center of component (s = flange_a.s + L/2 = flange_b.s - L/2)";\n')
        f.write('          parameter SI.Length L(start=0)\n')
        f.write('          "Length of component, from left flange to right flange (= flange_b.s - flange_a.s)";\n')
        f.write('          Flange_a flange_a "Left flange of translational component";\n')
        f.write('          Flange_b flange_b "Right flange of translational component";\n')
        f.write('        equation\n')
        f.write('          flange_a.s = s - L/2;\n')
        f.write('          flange_b.s = s + L/2;\n')
        f.write('        end PartialRigid;\n')
        f.write('\n')
        f.write('        partial model PartialCompliant\n')
        f.write('        "Compliant connection of two translational 1D flanges"\n')
        f.write('\n')
        f.write('          Flange_a flange_a\n')
        f.write('          "Left flange of compliant 1-dim. translational component";\n')
        f.write('          Flange_b flange_b\n')
        f.write('          "Right flange of compliant 1-dim. translational component";\n')
        f.write('          SI.Distance s_rel(start=0)\n')
        f.write('          "relative distance (= flange_b.s - flange_a.s)";\n')
        f.write('          SI.Force f\n')
        f.write('          "force between flanges (positive in direction of flange axis R)";\n')
        f.write('\n')
        f.write('        equation\n')
        f.write('          s_rel = flange_b.s - flange_a.s;\n')
        f.write('          flange_b.f = f;\n')
        f.write('          flange_a.f = -f;\n')
        f.write('        end PartialCompliant;\n')
        f.write('      end Interfaces;\n')
        f.write('    end Translational;\n')
        f.write('  end Mechanics;\n')
        f.write('\n')
        f.write('  package Icons "Library of icons"\n')
        f.write('\n')
        f.write('    partial package Library "Icon for library"\n')
        f.write('\n')
        f.write('    end Library;\n')
        f.write('\n')
        f.write('    partial package Library2\n')
        f.write('    "Icon for library where additional icon elements shall be added"\n')
        f.write('\n')
        f.write('    end Library2;\n')
        f.write('  end Icons;\n')
        f.write('\n')
        f.write('  package SIunits\n')
        f.write('  "Library of type and unit definitions based on SI units according to ISO 31-1992"\n')
        f.write('    extends Modelica.Icons.Library2;\n')
        f.write('\n')
        f.write('    type Length = Real (final quantity="Length", final unit="m");\n')
        f.write('\n')
        f.write('    type Position = Length;\n')
        f.write('\n')
        f.write('    type Distance = Length (min=0);\n')
        f.write('\n')
        f.write('    type Velocity = Real (final quantity="Velocity", final unit="m/s");\n')
        f.write('\n')
        f.write('    type Acceleration = Real (final quantity="Acceleration", final unit="m/s2");\n')
        f.write('\n')
        f.write('    type Mass = Real (\n')
        f.write('        quantity="Mass",\n')
        f.write('        final unit="kg",\n')
        f.write('        min=0);\n')
        f.write('\n')
        f.write('    type Force = Real (final quantity="Force", final unit="N");\n')
        f.write('\n')
        f.write('    type TranslationalSpringConstant=Real(final quantity="TranslationalSpringConstant", final unit\n')
        f.write('        =                                                                                          "N/m");\n')
        f.write('  end SIunits;\n')
        f.write('end Modelica;\n')
        f.write('\n')
        f.write('model Ground\n')
        f.write('  Modelica.Mechanics.Translational.Components.Fixed fixed;\n')
        f.write('  Modelica.Mechanics.Translational.Interfaces.Flange_b flange_b;\n')
        f.write('equation\n')
        f.write('  connect(fixed.flange, flange_b);\n')
        f.write('end Ground;\n')
        f.close()
        return [filename]
    
